package com.fxu.framework.core.util;

import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fxu.framework.core.base.Cascade;
import com.fxu.framework.core.enums.BaseEnum;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类对象属性操作类
 * @author fangxu
 * @version 1.0.0
 */
public final class FieldUtil {
	/** 级联注解缓存 */
	private static final SimpleCache<Field, Cascade> CASCADE_CACHE = new SimpleCache<>();
	/** 字段注解缓存 */
	private static final SimpleCache<Field, Class<?>> FIELD_CLASS_CACHE = new SimpleCache<>();
	/** 类的ID字段缓存 */
	private static final Map<Class<?>, Field> CLASS_ID_CLASS_CACHE = new ConcurrentHashMap<>();
	/** 类的ID字段缓存 */
	private static final Map<Class<?>, Boolean> HAS_TXT_CLASS_CACHE = new ConcurrentHashMap<>();

	/** 级联注解缓存 */
	private static final List<String> CASCADE_CACHE_NULL = new ArrayList<>();

	/** 类的字段缓存 */
	private static final SimpleCache<String, String> FIElD_IN_CLASS_CACHE = new SimpleCache<>();

	/**
	 * <p>获得级联注解</p>
	 * @param field 字段
	 * @return 级联注解
	 */
	public static Cascade getCascade(Field field) {
		Cascade cascade = CASCADE_CACHE.get(field);
		if (null != cascade) {
			return cascade;
		}
		if (CASCADE_CACHE_NULL.contains(field.getName())) {
			return null;
		}
		cascade = field.getAnnotation(Cascade.class);
		if (cascade == null) {
			CASCADE_CACHE_NULL.add(field.getName());
			return null;
		}
		return CASCADE_CACHE.put(field, cascade);
	}

	/**
	 * <p>获得字段类</p>
	 * @param field 字段
	 * @return 字段类
	 */
	public static Class<?> getFieldClass(Field field) {
		Class<?> clazz = FIELD_CLASS_CACHE.get(field);
		if (null != clazz) {
			return clazz;
		}
		Type fieldType = TypeUtil.getType(field);
		if (fieldType == null || TypeUtil.getTypeArgument(fieldType) == null
			|| TypeUtil.getClass(TypeUtil.getTypeArgument(fieldType)) == null) {
			FIELD_CLASS_CACHE.put(field, Void.class);
			return null;
		}
		return FIELD_CLASS_CACHE.put(field, TypeUtil.getClass(TypeUtil.getTypeArgument(fieldType)));
	}

	/**
	 * 是否含有text或者blob字段
	 * @param clazz Entity类
	 * @param <T> clazz范型类别
	 * @return 含有返回true,否则返回false
	 */
	public static <T> boolean hasTxtField(Class<T> clazz) {
		if (HAS_TXT_CLASS_CACHE.containsKey(clazz)) {
			return HAS_TXT_CLASS_CACHE.get(clazz);
		}
		Field field = BaseEnum.findOne(ReflectUtil.getFields(clazz), f -> {
			TableField tableField = f.getAnnotation(TableField.class);
			return tableField != null && !tableField.select();
		});
		if (field != null) {
			HAS_TXT_CLASS_CACHE.put(clazz, true);
			return true;
		}
		return false;
	}

	/**
	 * 获得Entity类自增字段
	 * @param clazz Entity类
	 * @param <T> clazz范型类别
	 * @return 自增字段
	 */
	public static <T> Field getPriKeyField(Class<T> clazz) {
		if (CLASS_ID_CLASS_CACHE.containsKey(clazz)) {
			return CLASS_ID_CLASS_CACHE.get(clazz);
		}
		Field field = BaseEnum.findOne(ReflectUtil.getFields(clazz), f -> f.getAnnotation(TableId.class) != null);
		if (field != null) {
			CLASS_ID_CLASS_CACHE.put(clazz, field);
			return field;
		}
		throw new RuntimeException("请为id字段设置@TableId!");
	}

	/**
	 * 获得Entity类自增字段的值
	 * @param entity Entity对象
	 * @param <T> entity范型类别
	 * @return 自增字段值
	 */
	public static <T> Object getPriKeyFieldVal(T entity) {
		if (null == entity || null == entity.getClass()) {
			return null;
		}
		Field keyField = FieldUtil.getPriKeyField(entity.getClass());
		return ReflectUtil.getFieldValue(entity, keyField);
	}

	/**
	 * 获得Entity类ID字段名
	 * @param clazz Entity类
	 * @param <T> clazz范型类别
	 * @return 主键字段名
	 */
	public static <T> String getPriKeyName(Class<T> clazz) {
		return getPriKeyField(clazz).getName();
	}

	/**
	 * 驼峰命名转化成下划线命名 UserRole to user_role
	 * @param bean 表名或者字段名
	 * @return bean对应的字段名
	 */
	public static String getColumnByBean(String bean) {
		if (bean == null || bean.length() <= 0) {
			return null;
		}
		final int len = bean.length();
		StringBuilder buffer = new StringBuilder(len + 3);
		for (int i = 0; i < len; i++) {
			if (Character.isUpperCase(bean.charAt(i))) {
				if (i != 0) {
					buffer.append("_");
				}
				buffer.append(Character.toLowerCase(bean.charAt(i)));
			} else {
				buffer.append(bean.charAt(i));
			}
		}
		return buffer.toString();
	}

	/**
	 * 驼峰命名转化成下划线命名 UserRole to user_role
	 * @param clazz 字段所属类
	 * @param bean 表名或者字段名
	 * @return bean对应的字段名
	 */
	public static String getColumnByBean(Class<?> clazz, String bean) {
		if (bean == null || bean.length() <= 0) {
			return null;
		}
		String key = clazz.getName() + "." + bean;
		String value = FIElD_IN_CLASS_CACHE.get(key);
		if (null != value) {
			return "_null_".equals(value) ? null : value;
		}
		value = getColumnByBean(bean);
		Field field = ReflectUtil.getField(clazz, bean);
		if (field != null) {
			TableField tableField = field.getAnnotation(TableField.class);
			if (tableField != null && StrUtil.isNotEmpty(tableField.value())) {
				value = tableField.value();
			}
		}
		value = value == null ? "_null_" : value;
		return FIElD_IN_CLASS_CACHE.put(key, value);
	}
}
